package com.sxpi.zixun.aspect.log;

import com.google.common.util.concurrent.RateLimiter;
import com.sxpi.zixun.common.enums.AdminUserResultEnum;
import com.sxpi.zixun.common.ex.PugBusinessException;
import com.sxpi.zixun.common.utils.ip.AddressUtils;
import com.sxpi.zixun.common.utils.ip.IpUtils;
import com.sxpi.zixun.common.utils.ip.UserAgentUtil;
import com.sxpi.zixun.common.utils.ip.Visit;
import com.sxpi.zixun.local.UserThreadLocal;
import com.sxpi.zixun.pojo.AdminLogs;
import com.sxpi.zixun.pojo.AdminUser;
import com.sxpi.zixun.service.adminlogs.IAdminLogsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;

/**
 * @Author: 董伟豪
 * @Date: 2022/9/6 19:33
 */
@Component
@Slf4j
@Aspect
@RequiredArgsConstructor
public class PugLogAspect {

    private final IAdminLogsService adminLogsService;

    /**
     * 这个没有维度的判断，后续可以根据ip进行判断，每个ip每秒访问一次
     * 限流每秒1次。如果设置为n，则每秒限流为1/n
     *
     * 这里面就会有一个预热模型，第一加载的数据不作数(但是还是算成功的请求)，拿一个令牌去尝试一下
     */
    private RateLimiter rateLimiter= RateLimiter.create(1);

    @Pointcut("@annotation(com.sxpi.zixun.aspect.log.PugLog)")
    public void pointCut(){

    }

    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature();
        // 获取执行的方法
        Method method = signature.getMethod();
        // 获取方法的注解
        PugLog annotation = method.getAnnotation(PugLog.class);
        try{
            // 前置增强
            long startTime = System.currentTimeMillis();
            // 如果这里写th事rows的话，那么如果在务上面出现这个注解，并且报错了的话，事务就会发生不回滚(这里把事务的异常吃掉了)
            Object proceed = proceedingJoinPoint.proceed();
            // 后置增强
            long endTime = System.currentTimeMillis() - startTime;
            // 这里就保存日志---观察者设计模式
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            HttpServletResponse response = requestAttributes.getResponse();
            AdminUser adminUser = UserThreadLocal.get();
            // 保存日志
            saveAdminLogs(endTime, method, annotation, request,adminUser);
            return proceed;
        }catch (Throwable ex) {
            System.err.println(ex.getMessage());
            throw new PugBusinessException(AdminUserResultEnum.USER_SERVER_ERROR);
        }

    }

    /**
     * 保存日志
     * @param endTime 方法执行时间
     * @param method 执行方法
     * @param annotation 执行方法上注解
     * @param request 当前请求
     * @param adminUser 当前用户
     */
    private void saveAdminLogs(long endTime, Method method, PugLog annotation, HttpServletRequest request, AdminUser adminUser) {
        // 获取令牌
        Boolean flag = rateLimiter.tryAcquire();
        if(!flag){
            throw new RuntimeException("被限流了");
        }


        AdminLogs adminLogs = new AdminLogs();
        adminLogs.setMethodname(method.getName());
        adminLogs.setClassname(method.getDeclaringClass().getName());
        adminLogs.setRequestmethod(request.getMethod());
        adminLogs.setMethodtime(endTime);
        adminLogs.setRequesturi(request.getRequestURI());
        adminLogs.setModel(annotation.value());
        adminLogs.setDescription(annotation.desc());

        if(adminUser != null){
            adminLogs.setUserId(adminUser.getId());
            adminLogs.setUsername(adminUser.getUsername());
            adminLogs.setTelephone(adminUser.getTelephone());
        }

        // 获取参数
        Parameter[] parameters = method.getParameters();
        if(parameters.length > 0){
            adminLogs.setMethodparams(parameters.toString());
        }

        try{
            String ip = IpUtils.getIpAddr(request);
            Map<String, String> realAddressByIPForMap = AddressUtils.getRealAddressByIPForMap(ip);
            // 获取省份
            adminLogs.setProvince(realAddressByIPForMap.get("province"));
            // 获取城市
            adminLogs.setCity(realAddressByIPForMap.get("city"));
            // 获取请求ip
            adminLogs.setRequestip(ip);
            // 根据请求对象获取相关请求的浏览器和操作系统信息
            Visit userAgent = UserAgentUtil.getUserAgent(request);
            // 获取浏览器类型
            adminLogs.setRequestbrower(userAgent.getBrowserType());
            // 获取操作系统
            adminLogs.setOsversion(userAgent.getPlatformType());
        }catch (Exception ex){
            adminLogs.setProvince("");
            adminLogs.setCity("");
            adminLogs.setRequestip("未知");
            adminLogs.setRequestbrower("未知");
            adminLogs.setOsversion("未知");
        }
        // 保存信息
        adminLogsService.saveUpdateAdminLogs(adminLogs);
    }


}
